﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using EntityFramework.Extensions;
using EntityFramework.Future;

namespace Mall.Data
{
    public partial class BaseRepository<T>
        where T : class
    {
        //获取当前线程内部的上下文实例
        private DbContext db = EFContextFactory.GetCurrentDbContext();

        #region 查询

        /// <summary>
        /// 根据过滤条件获取记录数
        /// </summary>
        public int GetCount(Expression<Func<T, bool>> exp = null)
        {
            return Filter(exp).Count();
        }


        /// <summary>
        /// 查询集合
        /// </summary>
        /// <param name="wherelambda">条件</param>
        /// <param name="noTracking">是否“不跟踪”查询（默认true,提高查询效率，但不能执行CUD）</param>
        /// <returns></returns>
        public IQueryable<T> LoadEntities(Expression<Func<T, bool>> wherelambda = null, bool noTracking = true)
        {
            return Filter(wherelambda, noTracking);
        }

        /// <summary>
        /// 查询单个对象
        /// </summary>
        /// <param name="wherelambda">条件</param>
        /// <param name="noTracking">是否“不跟踪”查询（默认true,提高查询效率，但不能执行CUD）</param>
        /// <returns></returns>
        public T LoadEntity(Expression<Func<T, bool>> wherelambda = null, bool noTracking = true)
        {
            return Filter(wherelambda, noTracking).FirstOrDefault();
        }

        /// <summary>
        /// 分页
        /// </summary>
        /// <typeparam name="S">排序字段</typeparam>
        /// <param name="pageSize">页大小</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="total">总数</param>
        /// <param name="whereLambda">条件</param>
        /// <param name="isAsc">排序方式</param>
        /// <param name="orderByLambda">排序筛选</param>
        /// <returns></returns>
        public IQueryable<T> LoadPagerEntities<S>(int pageSize, int pageIndex, out int total,
            Expression<Func<T, bool>> whereLambda, bool isAsc, Expression<Func<T, S>> orderByLambda)
        {
            var tempData = db.Set<T>().Where<T>(whereLambda);

            total = tempData.Count();

            //排序获取当前页的数据
            if (isAsc)
            {
                tempData = tempData.OrderBy<T, S>(orderByLambda).
                      Skip<T>(pageSize * (pageIndex - 1)).
                      Take<T>(pageSize).AsQueryable();
            }
            else
            {
                tempData = tempData.OrderByDescending<T, S>(orderByLambda).
                     Skip<T>(pageSize * (pageIndex - 1)).
                     Take<T>(pageSize).AsQueryable();
            }
            return tempData.AsQueryable();
        }



        /// <summary>
        /// 查询对象是否存在
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exp"></param>
        /// <returns></returns>
        public bool IsExist(Expression<Func<T, bool>> exp)
        {
            return db.Set<T>().Any(exp);
        }


        /// <summary>
        /// 查询过滤
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exp"></param>
        /// <param name="noTracking">是否“不跟踪”查询（默认true,提高查询效率，但不能执行CUD）</param>
        /// <returns></returns>
        private IQueryable<T> Filter(Expression<Func<T, bool>> exp, bool noTracking = true)
        {
            var dbSet = db.Set<T>().AsQueryable();
            if (noTracking)
            {
                dbSet = dbSet.AsNoTracking();
            }
            if (exp != null)
                dbSet = dbSet.Where(exp);
            return dbSet;
        }


        #endregion


        #region 新增

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public T AddEntity(T entity, bool change = true)
        {
            db.Set<T>().Add(entity);
            if (change)
            {
                if (db.SaveChanges() > 0)
                {
                    return entity;
                }
            }
            else
            {
                return entity;
            }
            return null;
        }

        /// <summary>
        /// 同时增加多条数据到一张表
        /// </summary>
        /// <param name="entitys"></param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool AddEntities(List<T> entitys, bool change = true)
        {
            db.Set<T>().AddRange(entitys);
            return SaveChanges(change);
        }

        #endregion


        #region 修改

        /// <summary>
        /// 修改单个实体
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool Update(T entity, bool change = true)
        {
            db.Set<T>().Attach(entity);
            db.Entry<T>(entity).State = EntityState.Modified;
            //db.Entry<T>(entity).State= EntityState.Unchanged;
            return SaveChanges(change);
        }

        /// <summary>
        /// 按指定条件更新实体,会更新整个实体（批量）,数据存在就更新，数据不存在就添加
        /// eg:Update(o=>o.col1,new obj(){xxxx},true)
        /// </summary>
        /// <param name="identityExp">The identity exp.</param>
        /// <param name="entity">The entity.</param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool Update(Expression<Func<T, object>> identityExp, bool change = true, params T[] entity)
        {
            db.Set<T>().AddOrUpdate(identityExp, entity);
            return SaveChanges(change);
        }
        /// <summary>
        /// 修改部分字段的值,指定列的值
        /// </summary>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <param name="entity">要修改的实体对象</param>
        /// <param name="proNames">要修改的属性名称</param>
        /// <returns></returns>
        public bool Update(T entity, bool change = true, params string[] proNames)
        {
            DbEntityEntry<T> dbee = db.Entry<T>(entity);
            if (dbee.State == EntityState.Detached)
            {
                db.Set<T>().Attach(entity);
            }
            dbee.State = EntityState.Unchanged;//先将所有属性状态标记为未修改
            proNames.ToList().ForEach(c => dbee.Property(c).IsModified = true);//将要修改的属性状态标记为修改

            return SaveChanges(change);
        }

        /// <summary>
        /// 批量修改
        /// </summary>
        /// <param name="entitys">修改的所有实体对象集合</param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool Update(List<T> entitys, bool change = true)
        {
            entitys.ForEach(entity =>
            {
                db.Set<T>().Attach(entity);
                db.Entry<T>(entity).State = System.Data.Entity.EntityState.Modified;//将所有属性标记为修改状态
            });
            return SaveChanges(change);
        }

        #endregion


        #region 删除
        /// <summary>
        /// 删除实体对象
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool Delete(T entity, bool change = true)
        {
            db.Set<T>().Remove(entity);
            return SaveChanges(change);
        }

        #endregion


        #region EF扩展方法，批量操作

        /// <summary>
        /// 批量删除所有符合表达式条件的数据
        /// 
        /// eg:base.DeleteByExtended(o=>o.id==1)
        /// </summary>
        /// <param name="wherelambda">条件表达式</param>
        /// <returns></returns>
        public int DeleteByExtended(Expression<Func<T, bool>> wherelambda = null)
        {
            if (wherelambda == null)
            {
                wherelambda = x => true;
            }
            return db.Set<T>().Delete(wherelambda);
        }


        /// <summary>
        /// 批量修改符合条件的数据
        /// 
        /// eg:base.UpdateByExtended(o=>new obj{status=0},o=>o.price>5)
        /// 
        /// </summary>
        /// <param name="obj">修改的字段</param>
        /// <param name="wherelambda">条件表达式</param>
        /// <returns></returns>
        public int UpdateByExtended(Expression<Func<T, T>> obj, Expression<Func<T, bool>> wherelambda = null)
        {
            if (wherelambda == null)
            {
                wherelambda = x => true;
            }
            return db.Set<T>().Update(wherelambda, obj);
        }


        /// <summary>
        /// 批量查询数据库,统计数量【FutureCount、FutureFirstOrDefault、FutureList事宜配合使用】
        /// </summary>
        /// <param name="wherelambda"></param>
        /// <returns></returns>
        public int FutureCount(Expression<Func<T, bool>> wherelambda = null)
        {
            if (wherelambda == null)
            {
                wherelambda = x => true;
            }
            return db.Set<T>().Where(wherelambda).FutureCount();
        }
        /// <summary>
        /// 批量查询数据库,查询单个实体【FutureCount、FutureFirstOrDefault、FutureList事宜配合使用】
        /// </summary>
        /// <param name="wherelambda"></param>
        /// <returns></returns>
        public T FutureFirstOrDefault(Expression<Func<T, bool>> wherelambda = null)
        {
            if (wherelambda == null)
            {
                wherelambda = x => true;
            }
            return db.Set<T>().Where(wherelambda).FutureFirstOrDefault();
        }

        /// <summary>
        /// 批量查询数据库,查询符合条件的记录【FutureCount、FutureFirstOrDefault、FutureList事宜配合使用】
        /// </summary>
        /// <param name="wherelambda"></param>
        /// <returns></returns>
        public FutureQuery<T> FutureList(Expression<Func<T, bool>> wherelambda = null)
        {
            if (wherelambda == null)
            {
                wherelambda = x => true;
            }
            return db.Set<T>().Where(wherelambda).Future();
        }

        #endregion


        #region 执行原生的sql语句，返回 表、受影响的行数等

        /// <summary>
        /// 执行数据源语句(如SQL)，返回影响的行数
        /// </summary>
        /// <param name="commandText">查询语句</param>
        /// <param name="paramete">参数(可选)</param>
        /// <returns></returns>
        public int ExecuteStoreCommand(string commandText, params Object[] paramete)
        {
            if (string.IsNullOrEmpty(commandText))
            {
                return -1;
            }
            return db.Database.ExecuteSqlCommand(commandText, paramete);
        }

        /// <summary>
        /// 执行数据源查询语句(如SQL)，获得数据查询列表
        /// </summary>
        /// <param name="commandText">查询语句</param>
        /// <param name="paramete">参数(可选)</param>
        /// <returns></returns>
        public IEnumerable<T> ExecuteStoreQuery(string commandText, params Object[] paramete)
        {
            var result = db.Database.SqlQuery<T>(commandText, paramete);
            return result;
        }

        #endregion


        /// <summary>
        /// 批量提交，变更数据库；将所有操作一次性保存会数据库，避免频繁连接和操作数据库[UintWork单元工作模式]
        /// </summary>
        /// <param name="change">是否 db.SaveChanges</param>
        /// <returns></returns>
        public bool SaveChanges(bool change = true)
        {

            if (!change)
            {
                return true;
            }

            try
            {
                //调用EF上下文的SaveChanges的方法
                return db.SaveChanges() > 0;
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }
    }

    public class EFContextFactory
    {
        #region 公共方法

        /// <summary>
        /// 返回当前线程内的数据库上下文
        /// </summary>
        /// <returns></returns>
        public static DbContext GetCurrentDbContext()
        {
            DbContext dbcontext = CallContext.GetData("DbContext") as DbContext;

            //判断线程里面是否有数据
            if (dbcontext == null)
            {
                dbcontext = new FlashShoppingMallEntities();  //创建了一个EF上下文
                //存储指定对象
                CallContext.SetData("DbContext", dbcontext);
            }
            return dbcontext;
        }

        #endregion 公共方法
    }
}